package com.google.code.cisx.servlets;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.inject.Inject;
import com.google.inject.Injector;

/**
 * Servlet that sets up resources for development mode.
 * <p>
 * <b>NOTE that this uses the OpenJPA {@link OpenJPAEntityManager}, hence not
 * portable across different JPA Provider implementations.</b>
 * 
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @since 1.0
 * @version $Id: DevelopmentServlet.java 141 2008-01-25 08:32:07Z
 *          rahul.thakur.xdev $
 */
public class DevelopmentServlet extends HttpServlet implements Servlet {

  /**
   * Serial version id.
   */
  private static final long serialVersionUID = -4573753142047412568L;

  /**
   * Commons Logging.
   */
  private static final Log log = LogFactory.getLog(DevelopmentServlet.class);

  @Inject
  private EntityManager em;

  /**
   * Determines if the application is running development mode or not.
   * <code>true</code> if the development mode is turned <b>ON</b>.
   */
  private String INIT_PARAM_DEV_MODE = "development-mode";

  /**
   * SQL resources to load.
   */
  private String INIT_PARAM_SQL_RESOURCES = "sql-resources";

  /**
   * Determines if an in-memory database is to be used.
   */
  private boolean isDevelopmentMode = true;

  /**
   * @see Servlet#init(ServletConfig)
   */
  public void init(ServletConfig config) throws ServletException {
    super.init();
    String devMode = config.getInitParameter(INIT_PARAM_DEV_MODE);

    if (null != devMode)
      isDevelopmentMode = Boolean.parseBoolean(devMode);

    if (log.isInfoEnabled())
      log.info("'development-mode' = " + isDevelopmentMode);

    if (isDevelopmentMode) {
      Injector registeredInjector = (Injector) config.getServletContext().getAttribute(
          ServletContextConstants.KEY_GUICE_INJECTOR);

      if (null == registeredInjector)
        throw new IllegalStateException(Injector.class.getName()
            + " is not yet registered in the ServletContext");
      registeredInjector.injectMembers(this);

      if (log.isDebugEnabled())
        log.debug("Loading SQL resources for 'development-mode'");
      loadSQLResources(config);
      if (log.isDebugEnabled())
        log.debug("Finished loading SQL resources for 'development-mode'");
    }
  }

  /**
   * Loads up the SQLs in an in-memory database for quick testing.
   * 
   * @param config
   */
  private void loadSQLResources(ServletConfig config) {
    // seed the in-memory database
    // TODO: Parameterize the SQLs.
    // String[] sqlFiles = {
    // "project-table-data.sql", "project-group-table-data.sql",
    // "project-notifier-table-data.sql"};

    String[] sqlFiles = StringUtils.split(config.getInitParameter(INIT_PARAM_SQL_RESOURCES), ',');

    for (int i = 0; i < sqlFiles.length; i++) {
      String sql = config.getServletContext().getRealPath("WEB-INF")
          + System.getProperty("file.separator") + sqlFiles[i];
      File fSql = new File(sql);
      if (fSql.exists())
        loadSqlSource(fSql);
      else
        log.error("Unable to locate SQL : " + sql);
    }
  }

  /**
   * Imports com.google.code.cisx.servlets from the specified file.
   * 
   * @param sqlResource Resource containing com.google.code.cisx.servlets
   */
  public void loadSqlSource(File sqlResource) {
    try {
      if (log.isDebugEnabled())
        log.debug("Loading com.google.code.cisx.servlets: " + sqlResource.getAbsolutePath());
      List<String> statements = new ArrayList<String>(20);
      BufferedReader br = new BufferedReader(
          new InputStreamReader(new FileInputStream(sqlResource)));
      String line = null;
      StringBuffer currentStatement = new StringBuffer();
      while ((line = br.readLine()) != null) {
        if (line.trim().length() == 0)
          continue;
        if (line.trim().startsWith("#"))
          continue;

        currentStatement.append(line);
        if (line.endsWith(";")) {
          statements.add(currentStatement.toString());
          currentStatement = new StringBuffer();
        }
      }
      // append a line if missing a ';'
      if (currentStatement.length() > 0) {
        statements.add(currentStatement.toString());
      }
      runSQLStatements(statements);
    } catch (Throwable e) {
      if (log.isErrorEnabled())
        log.error("Problem executing SQL!", e);
    }
  }

  /**
   * Run a bunch of SQL statements.
   * 
   * @param statements Statements to run.
   * @throws SQLException
   */
  public void runSQLStatements(final List<String> statements) throws SQLException {
    if (statements.size() > 0 && log.isDebugEnabled())
      log.debug("Importing SQL statments:");

    //OpenJPAEntityManager entityManager = OpenJPAPersistence.cast(em);
    Connection con = null;
    //con = (Connection) entityManager.getConnection();
    try {
      for (String qry : statements) {
        try {
          Statement stmt = con.createStatement();

          if (log.isDebugEnabled())
            System.out.println(qry);

          stmt.execute(qry);
          con.commit();
        } catch (SQLException e) {
          try {
            con.rollback();
          } catch (SQLException e1) {
            if (log.isErrorEnabled())
              log.error("Unable to rollback transaction.");
            throw e1;
          }
          throw e;
        }
      }
    } finally {
      if (null != con)
        con.close();
    }
    if (statements.size() > 0 && log.isDebugEnabled())
      log.debug("SQL import done!");
  }

}
